package de.justphil.tcg.tcgserver.rest.resources;

import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

import org.codehaus.jackson.map.AnnotationIntrospector;
import org.codehaus.jackson.map.AnnotationIntrospector.Pair;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig.Feature;
import org.codehaus.jackson.map.introspect.JacksonAnnotationIntrospector;
import org.codehaus.jackson.xc.JaxbAnnotationIntrospector;

import de.justphil.tcg.tcgserver.commons.domain.Card;
import de.justphil.tcg.tcgserver.commons.domain.User;
import de.justphil.tcg.tcgserver.commons.domain.complete.CCard;
import de.justphil.tcg.tcgserver.commons.domain.complete.CCardInstance;
import de.justphil.tcg.tcgserver.commons.domain.complete.CDeck;
import de.justphil.tcg.tcgserver.commons.domain.complete.CGame;
import de.justphil.tcg.tcgserver.commons.domain.complete.CProperty;
import de.justphil.tcg.tcgserver.commons.domain.complete.CUser;
import de.justphil.tcg.tcgserver.commons.domain.containers.NewGame;
import de.justphil.tcg.tcgserver.commons.domain.wrappers.CardInstanceListRes;
import de.justphil.tcg.tcgserver.commons.domain.wrappers.CreateDeck;
import de.justphil.tcg.tcgserver.commons.domain.wrappers.DeckListRes;
import de.justphil.tcg.tcgserver.commons.domain.wrappers.FreebieCard;
import de.justphil.tcg.tcgserver.commons.domain.wrappers.FreebieCardListRes;
import de.justphil.tcg.tcgserver.commons.domain.wrappers.ListRes;
import de.justphil.tcg.tcgserver.commons.domain.wrappers.NewGamePlayerOneRes;
import de.justphil.tcg.tcgserver.commons.domain.wrappers.NewGameRes;
import de.justphil.tcg.tcgserver.commons.domain.wrappers.ShallowCard;
import de.justphil.tcg.tcgserver.commons.domain.wrappers.Res;
import de.justphil.tcg.tcgserver.commons.domain.wrappers.ShallowCardInstance;
import de.justphil.tcg.tcgserver.commons.domain.wrappers.ShallowDeck;
import de.justphil.tcg.tcgserver.commons.domain.wrappers.SingleObjectRes;
import de.justphil.tcg.tcgserver.commons.domain.wrappers.UserListRes;

@Provider
public class JacksonObjectMapperProvider implements ContextResolver<ObjectMapper> {

    private final ObjectMapper defaultObjectMapper;
    private final ObjectMapper combinedObjectMapper;
    
    private Class<?>[] combinedTypes = {
    		Res.class,
    		NewGame.class,
    		NewGamePlayerOneRes.class,
    		NewGameRes.class,
    		CreateDeck.class,
    		Card.class,
    		ShallowCard.class,
    		ShallowCardInstance.class,
    		ShallowDeck.class,
    		CardInstanceListRes.class,
    		FreebieCardListRes.class,
    		DeckListRes.class,
    		FreebieCard.class,
    		User.class,
    		UserListRes.class,
    		SingleObjectRes.class,
    		ListRes.class,
    		CCard.class,
    		CCardInstance.class,
    		CDeck.class,
    		CGame.class,
    		CProperty.class,
    		CUser.class
    };

    public JacksonObjectMapperProvider() {
        defaultObjectMapper = createDefaultMapper();
        combinedObjectMapper = createCombinedObjectMapper();
    }

    @Override
    public ObjectMapper getContext(Class<?> objectType) {

    	for (Class<?> type : combinedTypes) {
			
			if (type == objectType) {
				return combinedObjectMapper;
			}
		}
    	
    	return defaultObjectMapper;
    }

    private static ObjectMapper createCombinedObjectMapper() {

        Pair combinedIntrospector = createJaxbJacksonAnnotationIntrospector();
        ObjectMapper result = new ObjectMapper();
        result.configure(Feature.WRAP_ROOT_VALUE, false);
        result.setDeserializationConfig(result.getDeserializationConfig().withAnnotationIntrospector(combinedIntrospector));
        result.setSerializationConfig(result.getSerializationConfig().withAnnotationIntrospector(combinedIntrospector));

        return result;
    }


    private static ObjectMapper createDefaultMapper() {

        ObjectMapper result = new ObjectMapper();
        result.configure(Feature.INDENT_OUTPUT, false);

        return result;
    }


    private static Pair createJaxbJacksonAnnotationIntrospector() {

    	AnnotationIntrospector primary = new JacksonAnnotationIntrospector();
        AnnotationIntrospector second = new JaxbAnnotationIntrospector();

        return new AnnotationIntrospector.Pair(primary, second);
    }
}
